﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace Apollo.Core
{
    public class Form
    {
        public Form() {
            this.AppendTabs = true;
            this.Groups = new List<FormGroup>();
        }

        public Entity Entity { get; set; }
        public IList<FormGroup> Groups { get; set; }
        
        /// <summary>
        /// If true it will allow application generated tabs.
        /// </summary>
        public bool AppendTabs { get; set; }

        public void SetFormType(FormItemType type) {
            if (this.Groups != null)
                foreach (FormGroup group in this.Groups)
                    group.SetFormType(type);
        }

        public XElement ToXml() {
            XElement xelem = new XElement("form");

            xelem.Add(new XAttribute("entityname", this.Entity.Name));
            xelem.Add(new XAttribute("entityid", this.Entity.Id));

            if (!this.AppendTabs) {
                xelem.Add(new XAttribute("appendtabs", "false"));
            }

            foreach (FormGroup group in this.Groups)
                xelem.Add(group.ToXml());

            return xelem;
        }

        public static Form GenerateDefault(Entity entity) {
            Form defaultForm = new Form() {
                Entity = entity
            };

            FormGroup defaultGroup = new FormGroup() {
                DisplayName = entity.Name,
                Form = defaultForm,
                Type = FormGroupType.Properties
            };

            defaultForm.Groups.Add(defaultGroup);

            int col = 0;
            int row = 0;
            int maxItemsPerRow = 3;

            bool implementsITrackable = (entity.MappedClass.GetInterface(typeof(ITrackable).Name) != null);
            bool implementsHistory = (entity.MappedClass.GetInterface(typeof(IHistory<>).Name) != null);

            foreach(EntityProperty property in Application.Meta<EntityProperty>().Where<EntityProperty>(ep => ep.Entity.Id == entity.Id)) {
                FormItemType propType = FormItemType.Normal;
                
                if (property.PropertyName == property.Entity.IdentifierProperty)
                    propType = FormItemType.ReadOnly;

                if (implementsITrackable &&
                    (property.PropertyName == "InsertDate" || property.PropertyName == "InsertBy" || property.PropertyName == "ModifiedDate" || property.PropertyName == "ModifiedBy"))
                    propType = FormItemType.ReadOnly;

                if (implementsHistory && (property.PropertyName == "LearnDate" || property.PropertyName == "BelongsTo"))
                    propType = FormItemType.ReadOnly;

                FormItem item = new FormItem()
                {
                    Group = defaultGroup,
                    Col = col,
                    Row = row,
                    Type = propType,
                    Projection = new Projection(property.PropertyName, entity.MappedClass)
                };

                defaultGroup.Items.Add(item);

                col++;

                if (col >= maxItemsPerRow) {
                    col = 0;
                    row++;
                }
            }

            return defaultForm;
        }

        public static Form GenerateFromString(string formString, Entity entity) {
            try
            {
                XDocument doc = XDocument.Parse(formString);

                Form form = new Form();

                form.Entity = entity;

                if (doc.Root.Name != "form")
                    throw new ArgumentException("Invalid XML format! Expected name 'form', but got '" + doc.Root.Name + "'");

                if (doc.Root.Attribute("appendtabs") != null) { 
                    form.AppendTabs = (doc.Root.Attribute("appendtabs").Value.ToLower() == "true");
                }


                foreach (XElement group in doc.Root.Elements("group"))
                    form.Groups.Add(FormGroup.FromXml(group, form));

                return form;
            }
            catch {
                return null;
            }
        }
    }
}
